/* kernel.h - disable, enable, halt, restore, isodd, min, max */
#ifndef KERNEL_H
#define KERNEL_H
#include <i386.h>

/* ======== UNIT TESTING PARAMETERS ================= */
#define KERNEL_VERBOSITY	FALSE

#define PREEMPTION_DIVISOR  1
#define DO_PREEMPTION		TRUE
#define PREEMPTION_UNIT_TEST TRUE

#define PRINT_REGS_ON_TRAP 	TRUE
#define REG_DISPLAY_DELAY	1*1000*1000

#define SCROLL_DELAY		0

#define MEM_UNIT_TEST 		FALSE 
#define MEM_UT_DELAY		000*1000
#define MEM_UT_NUM_ALLOC	20
#define MEM_UT_NUM_FREE		7
#define MEM_UT_SPACING		2
#define MEM_UT_MIN_SIZE		128*1024
#define MEM_UT_DO_RANDSIZE	TRUE

#define DISP_UNIT_TEST 		FALSE
#define DISP_UT_DELAY		10000
#define DISP_CYCLE_DELAY	0
#define DISP_UT_FAKE_PROCESSES FALSE
#define DISP_SHOW_QUEUES	TRUE
#define DISP_SHOW_PCB_TBL	TRUE
#define DISP_UT_SHOW_MEM	FALSE

#define SYSCALL_UT_DELAY	300*1000
#define SYSCALL_UNIT_TEST 	FALSE
#define SYSCALL_UT_SHOW_STACK FALSE


#define STOP_HAMMERTIME	for(;;);``

/* Other definitions */
#define ROOT_PROCESS_STACK_SIZE	1024

/* Debug macros */
#define verbose if(KERNEL_VERBOSITY) kprintf

/* Symbolic constants used throughout Xinu */
 
typedef	char		Bool;		/* Boolean type			*/
#define	FALSE		0		/* Boolean constants		*/
#define	TRUE		1
#define	EMPTY		(-1)		/* an illegal gpq		*/
#define	NULL		0		/* Null pointer for linked lists*/
#define	NULLCH		'\0'		/* The null character		*/


/* Universal return constants */

#define	OK		 	 1		/* system call ok		*/
#define	SYSERR		-1		/* system call failed		*/
#define	EOF			-2		/* End-of-file (usu. from read)	*/
#define	TIMEOUT		-3		/* time out  (usu. recvtim)	*/
#define	INTRMSG		-4		/* keyboard "intr" key pressed	*/
					/*  (usu. defined as ^B)	*/
#define	BLOCKERR	-5		/* non-blocking op would block	*/


typedef struct {
	/* Defines all registers in CPU to capture state. Used to initialize stack of a new process */
	uint32 		edi;
	uint32 		esi;
	uint32 		ebp;
	uint32 		esp;
	uint32 		ebx;
	uint32 		edx;
	uint32 		ecx;
	uint32 		eax;
	uint32		eip;
	uint32		cs;
	uint32		eflags;
} cpu;

typedef enum {
	NEW,
	RUNNING,
	READY,
	BLOCKED,
	DEAD,
	IDLE
} process_state;

/* Process control block */

struct queueElem;
typedef struct pcb
{
	/* This is a Process Control Block */
	int			 			pid;					/* Globally unique identifier */
	char*					name;					/* A friendly name that can be passed to create(). Not supported by syscreate() */
	process_state		 	state; 					/* Current status */
	uint8					priority;				/* Not used */
	uint16		 			parent_pid;				/* The pid of the process that called syscreate() to make this one */
	uint8*					stackPointer;			/* Current stack top in processes' allocated region */
	uint8*					memAllocPointer;		/* Start of allocated data region */
	uint32					sysCallReturnResult;	/* Storage for the return value of a system call */
	struct pcb *  			next;					/* The next PCB in the PCB table */
	uint32 					procEAX;				/* For the context switcher, an interrupt could blow away the proc's EAX. */	
	struct queueElem* 		msgQueue;				/* Queue of other procs waiting to send to this one. */
	void*					msgBuffer;
	int						msgLength;
	int						msgWaitingForPID;
} pcb;

typedef struct queueElem 
{
	/* 	This is needed since 'next' is queue specific. */
	pcb * 				processPointer;	/* pcb */
	struct queueElem * 	next;			/* Next queueElem in this queue */
} queueElem; 

typedef enum {
	SYSCREATE,
	SYSYIELD,
	SYSSTOP,
	SYSPUTS,
	SYSGETPID,
	SYSSEND,
	SYSRECV,
	TIMER_INT
} system_call;

/* Define global PCB and blockQueue pointers */
extern pcb* rootPCB;
extern pcb* idleprocPCB;
extern pcb* pcbTable;
extern queueElem* readyQueue;

/* Function definitions for globally available kernel functions: */
extern void kmeminit( void );
extern void *kmalloc( int size ); 	/* If the allocation succeeds, a pointer to the block of memory is 
									/* returned, otherwise a NULL pointer is returned. */
extern void kfree( void *ptr );		/* Takes pointer to previously allocated chunk and frees it. */
extern void printMemSpace();

extern void dispatch( void );							/* Infinite loop to schedule processes and handle system calls */
extern pcb* next();										/* Pop the first-in-line pcb from the ready queue */
extern int  ready( pcb* process);						/* Add the pcb to the end of the ready queue */
extern int block( pcb* process, queueElem* blockQueue); /* Place the process on the specified blockQueue. */
extern pcb* unblock(queueElem* blockQueue, int popPID); /* Pops the process off the specified blockQueue. Can specify any proc in queue. */
extern pcb* findInQueue(queueElem * queue, int findpid);/* Returns pointer to a process if it exists in a queue. Else returns NULL. */
extern void printPCB( pcb* indexedProc);				/* Prints a given pcb */
extern void printReadyQueue();							/* Prints the whole ready queue */

extern void contextInit();								/* Prepares interrupt */
extern system_call contextswitch( pcb* process );		/* Switches to the given process and returns the syscall */

extern unsigned int create( void (*func)(), unsigned int stackSize, char* process_name ); 
extern int cleanup( pcb * process); 					/* Sets a process to DEAD and removes it from all queues */ 
extern void printStackPointerArea(uint32* stackPointer, int numHigher, int numLower);

extern void root();										/* First user process */
extern void idleproc();
extern void consumer();
extern void producer();

extern uint32 syscall( system_call call, ... );	/* Handles all system calls */
extern int syscreate( void (*func)(), int stack ); 		/* Calls syscall to switch to kernel space and create */ 
extern int syscreateN( void (*func)(), int stack, char* name);
extern int sysyield( void ); 							/* Calls syscall to switch to kernel space and yield */
extern int sysstop( void ); 							/* Calls syscall to switch to kernel space and stop */
extern void sysputs( char* str ); 						/* Preemptable print function to screen */
extern int syssend( int to, void *buffer, int buffer_len );	/* Send data to another process. Returns -1 if fail, or the number of bytes sent. */
extern int sysrecv( int *from, void *buffer, int buffer_len ); /* 	Recieve data from another process. Blocks untill there is a message to recieve from ANY process. 
																	Returns num bytes receieved, else -1 on fail. 
																	The 'from' pointer value is changed to identify the sender process. */
extern int sysgetpid( void ); 							/* Returns pid of process to itself */

/* Send data to another process. Returns -1 if fail, or the number of bytes sent. 
Blocks untill the send operation completes. */
extern int send (int toPID, int fromPID, void* buffer, int buffer_len); 
/* 	Recieve data from another process. Blocks untill there is a message to recieve from ANY process. 
	Returns num bytes receieved, else -1 on fail. 
	The 'from' pointer value is changed to identify the sender process. */
extern int recv (int* fromPID, int toPID, void* buffer, int buffer_len);
#endif